# -*- encoding: utf-8 -*-
# lookup: for database views
############################################################################
#    Copyright (C) 2007 by Håvard Gulldahl                                 #
#    havard@gulldahl.no                                                    #
#                                                                          #
#    This program is free software; you can redistribute it and#or modify  #
#    it under the terms of the GNU General Public License as published by  #
#    the Free Software Foundation; either version 2 of the License, or     #
#    (at your option) any later version.                                   #
#                                                                          #
#    This program is distributed in the hope that it will be useful,       #
#    but WITHOUT ANY WARRANTY; without even the implied warranty of        #
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         #
#    GNU General Public License for more details.                          #
#                                                                          #
#    You should have received a copy of the GNU General Public License     #
#    along with this program; if not, write to the                         #
#    Free Software Foundation, Inc.,                                       #
#    59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             #
############################################################################

import logging, re, datetime
import jeje
from jeje import ejeje_journalpost, ejejeError

import storm.locals as storm
from storm.expr import And, Or, In, Select

class ejejeLookupError(ejejeError):
  pass

class ejeje_lookup(object):
  filters = []
  
  def __init__(self, store):
    self.store = store

  def search(self, search_string, limit=None):
    search_queries = self.parse_query(search_string)
    if not search_queries:
      return None
    jp = ejeje_journalpost
    ands = []
    for field, query in search_queries:
      if field in (None,):
        ands.append(Or(jp.tittel.like('%'+query+'%'),
                       jp.kommentar.like('%'+query+'%')))
      elif field.lower() in ('kommentar', 'tittel', 'sakskompleks'):
        ands.append(getattr(jp, field).like('%'+query+'%'))
      elif field.lower() in ('adressat', 'saksbehandler',):# 'sakskompleks'):
        property = getattr(jp, field)
        klass = getattr(jeje, 'ejeje_'+field)
        cols = self.store.find(klass, klass.navn.like('%'+query+'%'))
        if cols.count() > 0:
          ands.append(  Or(*(property == o for o in cols)) )
      elif field.lower() == 'retning':
        if not query[0].upper() in ('I', 'U', 'X', 'S'):
          raise ejejeLookupError('Unknown value for "retning": %s' % query)
        ands.append(jp.retning == query[0].upper())
      elif field.lower() == 'offentlig':
        boolmap = { '1': True,
                    'YES': True,
                    'Y':True,
                    'JA': True,
                    'J': True,
                    'TRUE': True,
                    '0': False,
                    'NO': False,
                    'N': False,
                    'NEI': False,
                    'FALSE': False }
        if not query.upper() in boolmap.keys():
          raise ejejeLookupError('Unknown value for "offentlig": %s' % query)
        ands.append(jp.offentlig == boolmap[query.upper()])
      elif field.lower() == 'lov':
        ands.append(jp.lovreferanse.like('%'+query+'%'))
      elif field.lower() == 'arkivsaksnr':
        ands.append(jp.arkivsaksnr.like('%'+query+'%'))
      elif field.lower() in ('datert', 'behandlet'):
        datequery = self.parse_datestring(query)
        if datequery is None:
          raise ejejeLookupError('Invalid format for datestring: %s' % query)
        f = { 'datert': jp.dokumentdato,
              'behandlet': jp.arkivdato }[field.lower()]
        if datequery['comp'] is not None: #greater-than or less-than date
          if datequery['comp'] == '<':
            ands.append(f < datequery['date1'])
          else:
            ands.append(f > datequery['date1'])
        elif datequery['date2'] is not None: # within daterange
          ands.append(f > datequery['date1'])
          ands.append(f < datequery['date2'])
        else: #exact date
          ands.append(f == datequery['date1'])
      elif field.lower() in ('organisasjon', 'type'):
        jo = jeje.ejeje_organisasjon
        if field.lower() == 'organisasjon':
          orgs = Select(jo.id, jo.navn.like('%'+query+'%'))
        else:
          orgs = Select(jo.id, jo.type == query)
        #orgids = [o.id for o in orgs]
        #print "!!!!!! ofudn orgs: %s" % orgids
        
        ands.append(In(jp.organisasjon_id, orgs))
      else:
        raise ejejeLookupError('No such search field: "%s"' % field)
    if not ands: #nothing to search for
      print "nothing to search for"
      return None
    q = And(*ands)
    res = self.store.find(ejeje_journalpost, q)
    return res

  def parse_query(self, query):
    """ saksbehandler:"Q Q" organisasjon:Q orgtype:Q adressat:Q Q -> storm expr"""
    struct = []
    rex = re.compile("""((\w+):)?(("([^"]+)")|('([^']+)')|[^\ ]+)""")
    while len(query) > 0:
      print "loking at %s" % query
      s = rex.search(query)
      if s is None:
        break
      field = s.group(2)
      subquery = s.group(3).lower()
      if s.group(7) is not None:
        subquery = s.group(7).lower()
      elif s.group(5) is not None:
        subquery = s.group(5).lower()
      struct.append( (field, subquery) )
      try:
        query = query[s.end(0):]
      except IndexError: # end of string
        break
    print "found searches: %s" % struct
    return struct

  def parse_datestring(self, string):
    """ [<|>] 2007 | 2007-12 | 2007-12-01 [-<date> ] -> storm expr"""
    rex = re.compile(r"(<|>)?(\d{4})(\.(\d{2})(\.(\d{2}))?)?(-(\d{4})(\.(\d{2})(\.(\d{2}))?)?)?")
    s = rex.match(string)
    if not s:
      return None
    print "datestfing:", s.groups()
    struct = { 'comp': s.group(1),
               #'date1': '-'.join([d for d in (s.group(2), s.group(4), s.group(6)) if d]),
               #'date2': '-'.join([d for d in (s.group(8), s.group(10), s.group(12)) if d]),
               'date1': datetime.date(*([int(d or 1) for d in (s.group(2), s.group(4), s.group(6))])),
               'date2': None,
             }
    if s.group(8) is not None: # second date also
      struct['date2'] = datetime.date(*([int(d or 1) for d in (s.group(8), s.group(10), s.group(12))]))

    print "parsed date:", struct
    return struct
